import time, base64, io, json
from PIL import Image
import cv2 as cv
import numpy as np
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains  # 这个类基本能够满足我们所有对鼠标操作的需求

def login(browser=None):   # 点击登录并输入账号密码
    browser.find_element_by_xpath('//*[@id="app"]/div/div[2]/div/a').click()  # 点击登录
    browser.find_element_by_xpath('//*[@id="login-username"]').send_keys('1373xxxxx')  # 输入账号
    browser.find_element_by_xpath('//*[@id="login-passwd"]').send_keys('zxxxxxx')   # 输入密码
    browser.find_element_by_xpath('//*[@id="geetest-wrap"]/div/div[5]/a[1]').click()    # 点击登录

def get_pic(browser=None):
    #  爬取需要验证的完整、缺失、填充图片
    broken_img = browser.execute_script('return document.getElementsByClassName("geetest_canvas_bg geetest_absolute")[0].toDataURL("image/png")')[22:]
    full_img = browser.execute_script('return document.getElementsByClassName("geetest_canvas_fullbg geetest_fade geetest_absolute")[0].toDataURL("image/png")')[22:]
    fill_img = browser.execute_script('return document.getElementsByClassName("geetest_canvas_slice geetest_absolute")[0].toDataURL("image/png")')[22:]

    broken_img_decode = base64.decodebytes(broken_img.encode())
    broken_image = Image.open(io.BytesIO(broken_img_decode))
    broken_image.save('login_verify_pic/broken_pic.png')

    full_img_decode = base64.decodebytes(full_img.encode())
    full_image = Image.open(io.BytesIO(full_img_decode))
    full_image.save('login_verify_pic/full_pic.png')

    fill_img_decode = base64.decodebytes(fill_img.encode())
    fill_image = Image.open(io.BytesIO(fill_img_decode))
    fill_image.save('login_verify_pic/fill_pic.png')
    print('图片保存成功')

def opencv_pic():
    bg = cv.imread('login_verify_pic/broken_pic.png')
    bg = cv.cvtColor(bg, cv.COLOR_BGR2GRAY)  # 将Blue、Green、Red转为Gray

    fil = cv.imread('login_verify_pic/fill_pic.png')
    fil = cv.cvtColor(fil, cv.COLOR_BGR2GRAY)
    fil = fil[fil.any(1)][:, 3:55]

    result = cv.matchTemplate(bg, fil, cv.TM_SQDIFF_NORMED)  # TM_SQDIFF_NORMED该方法使用归一化的平方差进行匹配，最佳匹配也在结果为0处
    max_loc = np.argmax(result)
    x, y = np.unravel_index(max_loc, result.shape)
    print('滑块需要移动的距离：',y)
    return y

def Verify_picture(browser=None, y=None):
    slide = browser.find_element_by_xpath('/html/body/div[2]/div[2]/div[6]/div/div[1]/div[2]/div[2]')  # 提取滑块
    action = ActionChains(browser)
    action.click_and_hold(slide).perform()  # 点击鼠标左键，不松开
    action.move_by_offset(y - 5, 0)
    action.pause(0.5)
    action.move_by_offset(1, 0)  # 拖拽到某个坐标然后松开
    action.pause(1)
    action.release()  # 在某个元素位置松开鼠标左键
    action.perform()  # 执行链中的所有动作
    time.sleep(2)

def get_cookie(browser=None):
    Cookies = browser.get_cookies()
    jsonCookies = json.dumps(Cookies)  # 转换成字符串保存
    with open(r'cookie/cookies.txt', 'w') as f:
        f.write(jsonCookies)

browser = webdriver.Chrome()
browser.get("https://www.bilibili.com/account/history")
login(browser)  # 点击登录并输入账号密码
time.sleep(0.5)
get_pic(browser)  # 爬取需要验证的完整、缺失、填充图片
time.sleep(1)
y = opencv_pic()  # 图片处理，获得滑块的移动距离
Verify_picture(browser, y)  # 验证登录
while True:
    if '退出' in browser.page_source:
        print('登录成功')
        get_cookie(browser)  # 获取cookie
        print('cookies保存成功！')
        break
    if '请正确拼合图像' in browser.page_source:
        print('登陆失败')
        browser.find_element_by_xpath('/html/body/div[2]/div[2]/div[6]/div/div[2]/div/a[2]').click()
        time.sleep(1)
        get_pic(browser)  # 爬取需要验证的完整、缺失、填充图片
        time.sleep(1)
        y = opencv_pic()  # 图片处理，获得滑块的移动距离
        Verify_picture(browser, y)  # 验证登录
        continue
